package com.zx.idc.backend.gui.sys;

import com.zx.idc.backend.gui.common.BaseApi;
import com.zx.idc.backend.gui.common.Result;
import com.zx.idc.backend.gui.common.constants.WebConstants;
import com.zx.idc.backend.gui.common.util.NetworkUtil;
import com.zx.idc.backend.gui.shiro.IncorrectCaptchaException;
import com.zx.idc.backend.gui.shiro.TokenManagerUtil;
import com.zx.idc.backend.gui.sys.login.MailClient;
import com.zx.idc.common.exception.GlobalException;
import com.zx.idc.common.exception.LoginException;
import com.zx.idc.common.util.IpUtil;
import com.zx.idc.common.util.ProcessUtil;
import com.zx.idc.ds.sys.entity.SysAuthIpSeg;
import com.zx.idc.ds.sys.entity.SysUser;
import com.zx.idc.ds.sys.service.ISysAuthIpSegService;
import com.zx.idc.ds.sys.service.ISysConfigurationService;
import com.zx.idc.ds.sys.service.ISysUserService;
import com.zx.idc.ds.sys.vo.SysUserVo;
import com.zx.idc.ds.tlog.entity.TLogActive;
import com.zx.idc.ds.tlog.entity.TLogSecurity;
import com.zx.idc.ds.tlog.service.ITLogActiveService;
import com.zx.idc.ds.tlog.service.ITLogSecurityService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;

/**
 * 系统登录控制器
 *
 * @author rxliuli
 */
@Controller
@RequestMapping("/system")
public class SystemLoginController extends BaseApi {

    private static final Logger LOG = LoggerFactory.getLogger(SystemLoginController.class);
    private static final String ENABLE = "2";
    private static final String VERIFY_CODE = "verifyCode";

    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private MailClient mailClient;
    @Autowired
    private ITLogSecurityService tlogSecurityService;
    @Autowired
    private TokenManagerUtil tokenManagerUtil;
    @Autowired
    private ITLogActiveService tlogActiveService;
    @Autowired
    private ISysConfigurationService sysConfigurationService;
    @Autowired
    private ISysAuthIpSegService sysAuthIpSegService;

    /**
     * 跳转到登录页面
     */
    @GetMapping("/login")
    public String login() {
        return "login";
    }

    /**
     * 获取邮件认证状态
     *
     * @return 是否邮件认证
     */
    @GetMapping("/get-verify")
    @ResponseBody
    public Result<Boolean> getVerify() {
        try {
            return success("1".equals(sysConfigurationService.getVal("sysSettings_login_mail_verify")));
        } catch (GlobalException e) {
            return error(e.getMessage());
        }
    }

    @GetMapping("/current-user")
    @ResponseBody
    public Result<SysUser> currentUser() {
        return success(tokenManagerUtil.getCurrentUser().setPassword(null));
    }

    /**
     * 进行登录操作
     *
     * @param param 用户名/密码
     * @return 是否登录成功
     */
    @PostMapping("/login")
    @ResponseBody
    public Result<Boolean> login(@RequestBody SysUserVo param, HttpServletRequest request) {
        SysUser validateUser = null;
        try {
            validateUser = sysUserService.selectOne(new SysUser().setLoginName(param.getLoginName()));
            final Object attribute = request.getSession().getAttribute(VERIFY_CODE);
            if(Objects.isNull(attribute)){
                tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "验证码已失效", ProcessUtil.getPid(), null));
                return error(400, "验证码已失效!");
            }
            if (!param.getCode().equalsIgnoreCase(((String)attribute))) {
                tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "验证码错误", ProcessUtil.getPid(), null));
                return error(400, "验证码错误!");
            }

            if (StringUtils.isAnyEmpty(param.getLoginName(), param.getPassword())) {
                tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "用户名或密码不能为空", ProcessUtil.getPid(), null));
                return error(400, "用户名或密码不能为空");
            }
            if (validateUser == null) {
                return error(400, "用户不存在");
            }
            if (validateUser.getStatus().equals(SysUser.STATUS_DISABLE)) {
                tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "用户已被禁用", ProcessUtil.getPid(), null));
                return error(403, "用户已被禁用");
            }

            String accessorIpAddress = NetworkUtil.getIpAddress(request);
            if (!accessorIpAddress.equals("0:0:0:0:0:0:0:1") && !accessorIpAddress.equals("127.0.0.1")) {
                String allowIpEnable = sysConfigurationService.getVal("allow_ip_enable");
                boolean allowIpStatus = false;
                if (StringUtils.isNotEmpty(allowIpEnable) && allowIpEnable.equals(ENABLE)) {
                    List<SysAuthIpSeg> sysAuthIpSegs = sysAuthIpSegService.listForAll();
                    for (SysAuthIpSeg sysAuthIpSeg : sysAuthIpSegs) {
                        if (sysAuthIpSeg.getAuthType().equals(SysAuthIpSeg.SINGLE_IP) && accessorIpAddress.equals(sysAuthIpSeg.getStartIp())) {
                            allowIpStatus = true;
                            break;
                        }
                        if (sysAuthIpSeg.getAuthType().equals(SysAuthIpSeg.IP_SEGMENT)
                            && IpUtil.ipExistsInRange(accessorIpAddress, sysAuthIpSeg.getStartIp(), sysAuthIpSeg.getEndIp())) {
                            allowIpStatus = true;
                            break;
                        }
                    }
                    if (!allowIpStatus) {
                        tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "用户不在授权IP范围内", ProcessUtil.getPid(), null));
                        return error(403, "用户不在授权IP范围内");
                    }
                }
            }

            tokenManagerUtil.login(param.getLoginName(), param.getPassword(), false, request.getRemoteHost(), param.getCode());
            SysUser loginUser = tokenManagerUtil.getCurrentUser();
            tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, loginUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "登录成功", ProcessUtil.getPid(), null));
            tlogActiveService.insertOrUpdate(new TLogActive(request.getSession().getId(), loginUser.getId(), LocalDateTime.now(), loginUser.getLastLoginTime(), NetworkUtil.getIpAddress(request)));

            return success(true);
        } catch (UnknownAccountException | IncorrectCredentialsException i) {
            tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "用户名或密码错误", ProcessUtil.getPid(), null));
            return error(400, "用户名或密码错误");
        } catch (LoginException e) {
            tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", e.getMessage(), ProcessUtil.getPid(), null));

            return error(e.getCode(), e.getMessage());
        } catch (IncorrectCaptchaException e) {
            tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", e.getMessage(), ProcessUtil.getPid(), null));
            return error(400, e.getMessage());
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGIN, validateUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登录", "系统错误", ProcessUtil.getPid(), null));
            return error(400, "系统错误");
        }
    }

    /**
     * 进行登出操作
     *
     * @return 是否登出成功
     */
    @PostMapping("/logout")
    @ResponseBody
    public Result<Boolean> logout(HttpServletRequest request) {
        SysUser sysUser = tokenManagerUtil.getCurrentUser();
        if (sysUser == null)
            return success(true);
        sysUserService.updateById(new SysUser().setId(sysUser.getId()).setLastLoginTime(LocalDateTime.now()));
        tlogSecurityService.insert(new TLogSecurity(TLogSecurity.LOGOUT, sysUser.getId(), NetworkUtil.getIpAddress(request), LocalDateTime.now(), "用户登出", "登出成功", ProcessUtil.getPid(), null));
        tokenManagerUtil.logout();
        return success(true);
    }


    @GetMapping("get-code")
    public void verifyCode(HttpServletResponse response, HttpServletRequest request) {
        try (OutputStream os = response.getOutputStream();) {

            int width = 200;

            int height = 50;

            BufferedImage verifyImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            //生成对应宽高的初始图片

            String randomText = drawRandomText(width, height, verifyImg);

            request.getSession().setAttribute(VERIFY_CODE, randomText.toLowerCase());

            response.setContentType("image/png");//必须设置响应内容类型为图片，否则前台不识别

            ImageIO.write(verifyImg, "png", os);//输出图片流

            os.flush();

        } catch (IOException e) {
            log.error("生成验证码异常", e);
        }
    }

    private static String drawRandomText(int width, int height, BufferedImage verifyImg) {

        Graphics2D graphics = (Graphics2D) verifyImg.getGraphics();

        graphics.setColor(Color.WHITE);//设置画笔颜色-验证码背景色

        graphics.fillRect(0, 0, width, height);//填充背景

        graphics.setFont(new Font("微软雅黑", Font.BOLD, 40));

        //数字和字母的组合

        String baseNumLetter = "123456789abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";

        StringBuilder sBuffer = new StringBuilder();

        int x = 10;  //旋转原点的 x 坐标

        String ch = "";

        Random random = new Random();

        for (int i = 0; i < 4; i++) {

            graphics.setColor(getRandomColor());

            //设置字体旋转角度

            int degree = random.nextInt() % 30;  //角度小于30度

            int dot = random.nextInt(baseNumLetter.length());

            ch = baseNumLetter.charAt(dot) + "";

            sBuffer.append(ch);

            //正向旋转

            graphics.rotate(degree * Math.PI / 180, x, 45);

            graphics.drawString(ch, x, 45);

            //反向旋转

            graphics.rotate(-degree * Math.PI / 180, x, 45);

            x += 48;

        }

        //画干扰线

        for (int i = 0; i < 6; i++) {

            // 设置随机颜色

            graphics.setColor(getRandomColor());

            // 随机画线

            graphics.drawLine(random.nextInt(width), random.nextInt(height),

                random.nextInt(width), random.nextInt(height));

        }

        //添加噪点

        for (int i = 0; i < 30; i++) {

            int x1 = random.nextInt(width);

            int y1 = random.nextInt(height);

            graphics.setColor(getRandomColor());

            graphics.fillRect(x1, y1, 2, 2);

        }

        return sBuffer.toString();

    }

    /**
     * 随机取色
     */

    private static Color getRandomColor() {

        Random ran = new Random();

        return new Color(ran.nextInt(256),

            ran.nextInt(256), ran.nextInt(256));

    }
}
